/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_AxisAlignedBox_h__
#define __dylab_AxisAlignedBox_h__

//===========================================================================================
#include <dylab/utils/geometry/Vector3.h>

//===========================================================================================
namespace dylab {

    /** 
     */
    class DYLAB_EXPORT AxisAlignedBox 
    {
    protected:        
        /// the bounding points
        Vector3 mMinPoint;
        Vector3 mMaxPoint;
        /// null flag
        bool mNull;
    public:

        /**          
         */
        AxisAlignedBox() 
            : mNull(true)
                { }

        /**
        */
        AxisAlignedBox(const AxisAlignedBox & _aabb) 
            : mMinPoint(_aabb.mMinPoint), mMaxPoint(_aabb.mMaxPoint), mNull(_aabb.mNull)
                { }

        /**
        */
        AxisAlignedBox(const Vector3 & _minPoint, const Vector3 & _maxPoint) 
            : mMinPoint(_minPoint), mMaxPoint(_maxPoint), mNull(false)
                { }


        /** resets the aabb
         */
        void reset()
                { 
                    mNull = true; 
                    mMinPoint = Vector3::ZERO;
                    mMaxPoint = Vector3::ZERO;
                }

        /** returns true if the aabb has not been set yet, otherwise returns false
         */
        bool isNull() const
                { return mNull; }

        /** returns the aabb's minimum point
         */
        Vector3 getMinPoint() const
                { 
                    DYLAB_ASSERT(!mNull); 
                    return mMinPoint; 
                }

        /** returns the aabb's maximum point
         */
        Vector3 getMaxPoint() const
                { 
                    DYLAB_ASSERT(!mNull);
                    return mMaxPoint; 
                }

        /**
        */
        Vector3 getExtents() const
                { return mMaxPoint - mMinPoint; }

        /**
        */
        void translate(const Vector3 & _v) 
                { 
                    DYLAB_ASSERT(!mNull); 
                    mMinPoint += _v, mMaxPoint += _v; 
                }

        /**
        */
        void scale(real_t _scale) 
                { 
                    DYLAB_ASSERT(!mNull); 
                    mMinPoint *= _scale, mMaxPoint *= _scale; 
                }

        /**
        */
        void merge(const Vector3 & _v)
                {
                    if (mNull)
                    {
                        mMinPoint = mMaxPoint = _v;
                        mNull = false;
                    }
                    else
                    {
                        if (_v.x() < mMinPoint.x()) mMinPoint.x() = _v.x();
                        if (_v.y() < mMinPoint.y()) mMinPoint.y() = _v.y();
                        if (_v.z() < mMinPoint.z()) mMinPoint.z() = _v.z();
                        if (_v.x() > mMaxPoint.x()) mMaxPoint.x() = _v.x();
                        if (_v.y() > mMaxPoint.y()) mMaxPoint.y() = _v.y();
                        if (_v.z() > mMaxPoint.z()) mMaxPoint.z() = _v.z();
                    }
                }

        /**
        */
        void extendMinPoint(real_t _d)
                { 
                    Vector3 v = mMinPoint - mMaxPoint;
                    v.normalise();
                    v *= _d;
                    mMinPoint -= v;
                }

        /**
        */
        void extendMaxPoint(real_t _d)
                { 
                    Vector3 v = mMaxPoint - mMinPoint;
                    v.normalise();
                    v *= _d;
                    mMaxPoint += v;
                }

        /**
        */
        void extend(real_t _d)
                { 
                    Vector3 v = mMaxPoint - mMinPoint;
                    v.normalise();
                    v *= _d;
                    mMinPoint -= v;
                    mMaxPoint += v; 
                }

        /**
        */
        bool testPointInside(const Vector3 & _p, bool _includingBoundary)
                { 
                    if (_includingBoundary)
                        return (_p >= mMinPoint) && (_p <= mMaxPoint); 
                    else
                        return (_p > mMinPoint) && (_p < mMaxPoint); 
                }

        /**
        */
        operator String() const
				{ return String::fmt("[%1% - %2%]", mMinPoint, mMaxPoint); }
    };

    //===========================================================================================    
    inline std::ostream & operator << (std::ostream & _os, const AxisAlignedBox & _o)
            { return _os << String(_o); }
}
//===========================================================================================
#endif // __dylab_AxisAlignedBox_h__
